package reports

import (
	"fmt"
	"io/ioutil"
	"sync"

	"gopkg.in/yaml.v2"
	"shqsoft.cn/pas3ui/base"
)

//Config 报表配置
type Config struct {
	Type            string          `yaml:"type" json:"-"`
	Name            string          `yaml:"name" json:"-"`
	KeyColumns      []string        `yaml:"keyColumns" json:"keyColumns"`
	OrderBy         string          `yaml:"orderBy" json:"-"`
	Where           string          `yaml:"where" json:"-"`
	Columns         []Column        `yaml:"columns" json:"columns"`
	Rows            []Column        `yaml:"rows" json:"rows"`
	Cells           []Column        `yaml:"cells" json:"cells"`
	Params          []Parameter     `yaml:"params" json:"params"`
	Groups          []Group         `yaml:"groups" json:"groups"`
	DataSourceType  string          `yaml:"dataSourceType" json:"-"`
	DataSource      DataSource      `yaml:"dataSource" json:"-"`
	AuthorityFilter AuthorityFilter `yaml:"authorityFilter" json:"-"`
	TitleColumns    []string        `yaml:"titleColumns" json:"titleColumns"`
	Chart           Chart           `yaml:"chart" json:"chart"`
	Unit            int             `yaml:"unit" json:"unit"`
	UnitTitle       string          `yaml:"unitTitle" json:"unitTitle"`
	HasTotal        bool            `yaml:"hasTotal" json:"hasTotal"`
	DrillDowns      []DrillDown     `yaml:"drillDowns" json:"drillDowns"`
	CalcCols        []CalcCol       `yaml:"calcCols" json:"calcCols"`
	HideParamBar    bool            `yaml:"hideParamBar" json:"hideParamBar"`
	ColumnLevels    []ColumnLevel   `yaml:"columnLevels" json:"columnLevels"`
	Remarks         string          `yaml:"remarks" json:"remarks"`
	Annotations     Annotations     `yaml:"annotations" json:"-"`
	NotFrontGroup   bool            `yaml:"notFrontGroup" json:"notFrontGroup"`
	MinDate         string          `yaml:"minDate" json:"-"`
	MaxDate         string          `yaml:"maxDate" json:"-"`
}

//Column 列定义
type Column struct {
	Title           string   `yaml:"title" json:"title"`
	Name            string   `yaml:"name" json:"key"`
	ColExp          string   `yaml:"colExp" json:"colExp"`
	DataIndex       string   `yaml:"dataIndex" json:"dataIndex"`
	Width           int      `yaml:"width" json:"width"`
	DataType        string   `yaml:"dataType" json:"dataType"`
	Format          string   `yaml:"format" json:"format"`
	Sorter          bool     `yaml:"sorter" json:"sorter"`
	Dispaly         bool     `yaml:"dispaly" json:"dispaly"`
	PopedomTypes    []string `yaml:"popedomTypes" json:"-"`
	PopedomRelation string   `yaml:"popedomRelation" json:"-"`
	Where           string   `yaml:"where" json:"-"`
	Filter          bool     `yaml:"filter" json:"filter"`
	Children        []Column `yaml:"children" json:"children"`
	GroupCalc       string   `yaml:"groupCalc" json:"groupCalc"`
	GroupExp        string   `yaml:"groupExp" json:"groupExp"`
	Ellipsis        bool     `yaml:"ellipsis" json:"ellipsis"`
	Align           string   `yaml:"align" json:"align"`
	Fixed           string   `yaml:"fixed" json:"fixed"`
	RefCol          string   `yaml:"refCol" json:"refCol"`
	FrontMust       bool     `yaml:"frontMust" json:"frontMust"`
	Unit            int      `yaml:"unit" json:"unit"`
	Subtotal        bool     `yaml:"subtotal" json:"subtotal"`
}

func (c *Column) Clone() *Column {
	return &Column{
		Title:           c.Title,
		Name:            c.Name,
		ColExp:          c.ColExp,
		DataIndex:       c.DataIndex,
		Width:           c.Width,
		DataType:        c.DataType,
		Format:          c.Format,
		Sorter:          c.Sorter,
		Dispaly:         c.Dispaly,
		PopedomTypes:    c.PopedomTypes,
		PopedomRelation: c.PopedomRelation,
		Where:           c.Where,
		Filter:          c.Filter,
		Children:        c.Children,
		GroupCalc:       c.GroupCalc,
		GroupExp:        c.GroupExp,
		Ellipsis:        c.Ellipsis,
		Align:           c.Align,
		Fixed:           c.Fixed,
		RefCol:          c.RefCol,
		FrontMust:       c.FrontMust,
		Unit:            c.Unit,
		Subtotal:        c.Subtotal,
	}
}

type ColumnLevel struct {
	IdCol    string `yaml:"idCol" json:"idCol"`
	NameCol  string `yaml:"nameCol" json:"nameCol"`
	OrderCol string `yaml:"orderCol" json:"orderCol"`
	HasTotal bool   `yaml:"hasTotal" json:"hasTotal"`
}

//Chart 图表
type Chart struct {
	Type   string `yaml:"type" json:"type"`
	Title  string `yaml:"title" json:"title"`
	Height int    `yaml:"height" json:"height"`
	XCol   string `yaml:"xCol" json:"xCol"`
	YCol   string `yaml:"yCol" json:"yCol"`
	ZCol   string `yaml:"zCol" json:"zCol"`
}

//Parameter 报表参数
type Parameter struct {
	Name         string     `yaml:"name" json:"name"`
	Caption      string     `yaml:"caption" json:"label" `
	DataType     string     `yaml:"dataType" json:"dataType"`
	InitVal      string     `yaml:"initVal" json:"initVal"`
	EditType     string     `yaml:"editType" json:"type"`
	Fetch        Fetch      `yaml:"fetch" json:"fetch"`
	DataItems    []DataItem `yaml:"dataItems" json:"data"`
	Placeholder  string     `yaml:"placeholder" json:"placeholder"`
	MultiSelect  bool       `yaml:"multiSelect" json:"multiSelect"`
	HasAll       bool       `yaml:"hasAll" json:"hasAll"`
	Grid         int        `yaml:"grid" json:"grid"`
	Layout       Layout     `yaml:"layout" json:"layout"`
	RefColName   string     `yaml:"refColName" json:"-"`
	RelationMark string     `yaml:"relationMark" json:"-"`
}

//Fetch 查找数据项
type Fetch struct {
	TableName     string   `yaml:"tableName" json:"tableName"`
	SearchColumns []string `yaml:"searchColumns" json:"searchColumns"`
	OptionTpl     string   `yaml:"optionTpl" json:"optionTpl"`
	ValueTpl      string   `yaml:"valueTpl" json:"valueTpl"`
	Where         string   `yaml:"where" json:"where"`
}

//DataItem 数据项
type DataItem struct {
	Value string `yaml:"value" json:"value"`
	Label string `yaml:"label" json:"label"`
}

//Layout 查询参数的页面布局
type Layout struct {
	LabelSpan   int `yaml:"labelSpan" json:"labelSpan"`
	WrapperSpan int `yaml:"wrapperSpan" json:"wrapperSpan"`
}

//DataSource 数据源
type DataSource struct {
	RefRepts     []RefRept `yaml:"refRepts"`
	DbName       string    `yaml:"dbName"`
	Type         string    `yaml:"type"`
	Table        string    `yaml:"table"`
	TmpTableName string    `yaml:"tmpTableName"`
	DFTable      string    `yaml:"dfTable"`
	MFTable      string    `yaml:"mfTable"`
	CacheType    string    `yaml:"cacheType"`
	Where        string    `yaml:"where"`
	Having       string    `yaml:"having"`
	SqlFileName  string    `yaml:"sqlFileName"`
}

//Annotations 附注
type Annotations struct {
	Title      string     `yaml:"title"`
	OrderBy    string     `yaml:"orderBy"`
	Columns    []Column   `yaml:"columns"`
	DataSource DataSource `yaml:"dataSource"`
}

//RefRept 依赖报表
type RefRept struct {
	Name   string            `yaml:"name"`
	Params map[string]string `yaml:"params" json:"params"`
}

//CallBack 回调
type CallBack struct {
	Type string `yaml:"type"`
	Name string `yaml:"name"`
}

type Group struct {
	Title          string   `yaml:"title" json:"title"`
	By             []string `yaml:"by" json:"by"`
	ExcludeCols    []string `yaml:"excludeCols" json:"excludeCols"`
	Caption        string   `yaml:"caption" json:"caption"`
	SubtitleCol    string   `yaml:"subtitleCol" json:"subtitleCol"`
	SubtitleSuffix string   `yaml:"subtitleSuffix" json:"subtitleSuffix"`
}

type DrillDown struct {
	Title        string            `yaml:"title" json:"title"`
	ReptName     string            `yaml:"reptName" json:"reptName"`
	Params       map[string]string `yaml:"params" json:"params"`
	Drawer       map[string]string `yaml:"drawer" json:"drawer"`
	RefColNames  []string          `yaml:"refColNames" json:"refColNames"`
	RefColWheres map[string]string `yaml:"refColWheres" json:"refColWheres"`
}

type CalcCol struct {
	Title       string   `yaml:"title" json:"title"`
	Name        string   `yaml:"name" json:"name"`
	ExcludeCols []string `yaml:"excludeCols" json:"excludeCols"`
	RefColTypes []string `yaml:"refColTypes" json:"refColTypes"`
}

//AuthorityFilter 权限过滤
type AuthorityFilter struct {
	FilterType string `yaml:"filterType"`
	ColName    string `yaml:"colName"`
}

var confMap sync.Map

func loadConfig(name string) (conf *Config, err error) {
	path, err := base.WorkPath()
	if err != nil {
		return
	}
	path = path + "/reportConfig/" + name + ".yaml"
	jsonBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	conf = &Config{}
	err = yaml.Unmarshal(jsonBytes, conf)
	if err != nil {
		return
	}
	confMap.Store(name, conf)
	return
}

//GetReportConfig 获取报表配置
func GetReportConfig(name string) (conf *Config, err error) {
	if obj, ok := confMap.Load(name); ok {
		conf, _ := obj.(*Config)
		return conf, nil
	}
	return loadConfig(name)
}

func DeleteReportCache(name string) {
	confMap.Delete(name)
}

func (c *Config) GetLeafColumns() (cols []*Column, err error) {
	clen := len(c.Columns)
	cols = make([]*Column, 0, clen)
	if c.Type == "cross" {
		for i := 0; i < clen; i++ {
			if len(c.Columns[i].Children) > 0 {
				err = fmt.Errorf("报表%s配置错误，cross报表的columns:%s不能有子column", c.Name, c.Columns[i].Name)
				return
			}
			cols = append(cols, &c.Columns[i])
		}
		clen = len(c.Rows)
		if clen == 0 {
			err = fmt.Errorf("报表%s配置错误，cross报表必须配置rows", c.Name)
			return
		}
		for i := 0; i < clen; i++ {
			if len(c.Rows[i].Children) > 0 {
				err = fmt.Errorf("报表%s配置错误，cross报表的rows:%s不能有子column", c.Name, c.Rows[i].Name)
				return
			}
			cols = append(cols, &c.Rows[i])
		}
		clen = len(c.Cells)
		if clen == 0 {
			err = fmt.Errorf("报表%s配置错误，cross报表必须配置cells", c.Name)
			return
		}
		for i := 0; i < clen; i++ {
			if len(c.Cells[i].Children) > 0 {
				err = fmt.Errorf("报表%s配置错误，cross报表的cells:%s不能有子column", c.Name, c.Cells[i].Name)
				return
			}
			if c.Cells[i].DataType != "float" && c.Cells[i].DataType != "int" {
				err = fmt.Errorf("报表%s配置错误，cross报表的cells中的字段的类型必须是int或者float", c.Name)
				return
			}
			cols = append(cols, &c.Cells[i])
		}
		return
	}
	for i := 0; i < clen; i++ {
		leafs := getLeafColumns(&c.Columns[i])
		cols = append(cols, leafs...)
	}
	return
}
func getLeafColumns(column *Column) []*Column {
	leafs := make([]*Column, 0, 5)
	if len(column.Children) == 0 {
		leafs = append(leafs, column)
	}
	for i := 0; i < len(column.Children); i++ {
		l2 := getLeafColumns(&column.Children[i])
		leafs = append(leafs, l2...)
	}
	return leafs
}
func (c *Config) GetCalcTitle(calcName string) string {
	for _, calc := range c.CalcCols {
		if calc.Name == calcName {
			return calc.Title
		}
	}
	return ""
}
